/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.project.imported;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.project.ImportConflictDTO;
import cn.ac.nercis.pes.model.standard.analyze.ResultCorrectionSDTO;
import cn.ac.nercis.pes.model.standard.asset.AssetSDTO;
import cn.ac.nercis.pes.model.standard.evaluation.EvaluationRecordSDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.ResultCorrectionRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.evaluation.ResultCorrectionDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.utils.CheckFieldUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 测评结果修正信息导入服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class ResultCorrectionImportService extends CommonImportService {
    private final ResultCorrectionRepository resultCorrectionRepository;
    private final AssetEvaluationMethodRepository evaluationMethodRepository;
    private final EvaluationRecordRepository evaluationRecordRepository;
    private final AssetRepository assetRepository;
    private final SystemTargetRepository systemTargetRepository;

    public ResultCorrectionImportService(ResultCorrectionRepository resultCorrectionRepository,
                                         AssetEvaluationMethodRepository evaluationMethodRepository,
                                         EvaluationRecordRepository evaluationRecordRepository,
                                         AssetRepository assetRepository,
                                         SystemTargetRepository systemTargetRepository) {
        this.resultCorrectionRepository = resultCorrectionRepository;
        this.evaluationMethodRepository = evaluationMethodRepository;
        this.evaluationRecordRepository = evaluationRecordRepository;
        this.assetRepository = assetRepository;
        this.systemTargetRepository = systemTargetRepository;
    }

    public Result<List<ImportConflictDTO>> checkResultCorrection(@NonNull String systemId, List<ResultCorrectionSDTO> resultCorrections,
                                                                 List<EvaluationRecordSDTO> evaluationRecords, List<AssetSDTO> assets) {
        List<ResultCorrectionDO> sourceData = this.resultCorrectionRepository.findAllBySystemId(systemId);
        if (CollectionUtils.isEmpty(sourceData)) {
            return Result.success();
        }
        List<EvaluationRecordDO> sourceRecords = sourceData.stream()
                .map(ResultCorrectionDO::getRecord)
                .collect(Collectors.toList());
        List<AssetEvaluationMethodDO> sourceMethods = this.evaluationMethodRepository.findAllBySystemId(systemId);
        List<AssetDO> sourceAssets = this.assetRepository.findAllBySystemId(systemId);
        List<SystemTargetDO> systemTargets = systemTargetRepository.findAllBySystemId(systemId);
        List<ImportConflictDTO> conflictData = resultCorrections.stream()
                .flatMap(ec -> this.checkResultCorrection(ec, sourceData, systemTargets,
                        findEvaluationRecord(ec.getRecordId(), evaluationRecords, assets, sourceAssets, sourceMethods, sourceRecords))
                        .stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        return Result.success(conflictData);
    }

    public Result<String> importResultCorrection(@NonNull String systemId, List<ResultCorrectionSDTO> resultCorrections,
                                                 List<EvaluationRecordSDTO> evaluationRecords, List<AssetSDTO> assets, Boolean cover) {
        List<ResultCorrectionDO> sourceData = this.resultCorrectionRepository.findAllBySystemId(systemId);
        List<EvaluationRecordDO> sourceRecords = this.evaluationRecordRepository.findAllBySystemId(systemId);
        List<AssetEvaluationMethodDO> sourceMethods = this.evaluationMethodRepository.findAllBySystemId(systemId);
        List<AssetDO> sourceAssets = this.assetRepository.findAllBySystemId(systemId);
        Result<String> result = Result.success();
        for (ResultCorrectionSDTO item : resultCorrections) {
            var evalRecord = findEvaluationRecord(item.getRecordId(), evaluationRecords, assets, sourceAssets, sourceMethods, sourceRecords);
            if(Objects.isNull(evalRecord)){
                continue;
            }
            result = this.importResultCorrection(systemId, item, sourceData, cover,evalRecord,
                    findEvaluationRecord(item.getOffsetRecordId(), evaluationRecords, assets, sourceAssets, sourceMethods, sourceRecords));
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private List<ImportConflictDTO> checkResultCorrection(@NonNull ResultCorrectionSDTO importData, List<ResultCorrectionDO> sourceData,
                                                          List<SystemTargetDO> systemTargets, EvaluationRecordDO sourceRecord) {
        if (Objects.isNull(sourceRecord)) {
            return Collections.emptyList();
        }
        ResultCorrectionDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId())
                        && StringUtils.equals(st.getUnitTargetId(), importData.getUnitTargetId())
                        && StringUtils.equals(st.getRecord().getId(), sourceRecord.getId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return Collections.emptyList();
        }
        List<ImportConflictDTO> conflictData = CheckFieldUtils.checkField(entity, importData);
        if (CollectionUtils.isNotEmpty(conflictData)) {
            String title = String.format("%s(%s)", sourceRecord.getAsset().getName(), getTargetName(importData.getUnitTargetId(), systemTargets));
            conflictData.add(0, ImportConflictDTO.builder()
                    .fieldName(BaseConstants.IMPORT_CONFLICT_DATA_TAG)
                    .newContent(title)
                    .oldContent(title)
                    .build());
        }
        return conflictData;
    }

    private Result<String> importResultCorrection(String systemId, @NonNull ResultCorrectionSDTO importData, List<ResultCorrectionDO> sourceData,
                                                  Boolean cover, @NonNull EvaluationRecordDO sourceRecord, EvaluationRecordDO offsetRecord) {
        ResultCorrectionDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId())
                        && StringUtils.equals(st.getUnitTargetId(), importData.getUnitTargetId())
                        && StringUtils.equals(st.getRecord().getId(), sourceRecord.getId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return createResultCorrection(systemId, importData, sourceRecord,offsetRecord,sourceData);
        } else {
            return modifyResultCorrection(entity, importData,offsetRecord, cover);
        }
    }

    private Result<String> createResultCorrection(String systemId, ResultCorrectionSDTO importData,
                                                  EvaluationRecordDO sourceRecord,EvaluationRecordDO offsetRecord,
                                                  List<ResultCorrectionDO> sourceData) {
        ResultCorrectionDO entity = ResultCorrectionDO.builder()
                .systemId(systemId)
                .layerTargetId(importData.getLayerTargetId())
                .unitTargetId(importData.getUnitTargetId())
                .offsetRecordId(Objects.nonNull(offsetRecord)?offsetRecord.getId():"")
                .result(importData.getResult())
                .score(importData.getScore())
                .desc(importData.getDesc())
                .finishState(importData.getFinishState())
                .record(sourceRecord)
                .build();
        entity.setCreateTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(this.resultCorrectionRepository::save)
                .map(data->{
                    sourceData.add(data);
                    return Result.success(data.getId());
                })
                .orElse(Result.failed("导入结果修正信息，创建失败"));
    }

    private Result<String> modifyResultCorrection(ResultCorrectionDO sourceData, ResultCorrectionSDTO importData,EvaluationRecordDO offsetRecord, Boolean cover) {
        var offsetRecordId = Objects.nonNull(offsetRecord)?offsetRecord.getId():"";
        sourceData.setResult(CheckFieldUtils.applyContent(sourceData.getResult(), importData.getResult(), cover));
        sourceData.setOffsetRecordId(CheckFieldUtils.applyContent(sourceData.getOffsetRecordId(),offsetRecordId,cover));
        sourceData.setScore(CheckFieldUtils.applyContent(sourceData.getScore(), importData.getScore(), cover));
        sourceData.setDesc(CheckFieldUtils.applyContent(sourceData.getDesc(), importData.getDesc(), cover));
        sourceData.setFinishState(CheckFieldUtils.applyContent(sourceData.getFinishState(), importData.getFinishState(), cover));
        sourceData.setModifyTime(LocalDateTime.now());
        return Optional.of(sourceData)
                .map(this.resultCorrectionRepository::save)
                .map(ResultCorrectionDO::getId)
                .map(Result::success)
                .orElse(Result.failed("导入结果修正信息，修改失败"));
    }

    private EvaluationRecordDO findEvaluationRecord(String recordId,
                                                    List<EvaluationRecordSDTO> evaluationRecords, List<AssetSDTO> assets,
                                                    List<AssetDO> sourceAssets, List<AssetEvaluationMethodDO> sourceMethods,
                                                    List<EvaluationRecordDO> sourceRecords) {
        var methodDO = evaluationRecords.stream()
                .filter(er -> StringUtils.equals(er.getId(), recordId))
                .findFirst()
                .map(r->this.findEvaluationMethod(r, assets, sourceAssets, sourceMethods))
                .orElse(null);
        if (Objects.isNull(methodDO)) {
            return null;
        }
        return sourceRecords.stream()
                .filter(st -> StringUtils.equals(st.getUnitTargetId(), methodDO.getUnitTargetId())
                        && StringUtils.equals(st.getAssetEvaMethodId(), methodDO.getId())
                        && StringUtils.equals(st.getAsset().getId(), methodDO.getAssetId()))
                .findFirst()
                .orElse(null);
    }

    private AssetEvaluationMethodDO findEvaluationMethod(EvaluationRecordSDTO record, List<AssetSDTO> assets,
                                                         List<AssetDO> sourceAssets, List<AssetEvaluationMethodDO> sourceMethods) {
        AssetDO assetDO = this.findAsset(record.getAssetId(), assets, sourceAssets);
        if (Objects.isNull(assetDO)) {
            return null;
        }
        return sourceMethods.stream()
                .filter(st -> StringUtils.equals(st.getClassId(), assetDO.getClassId())
                        && StringUtils.equals(st.getAssetId(), assetDO.getId())
                        && StringUtils.equals(st.getUnitTargetId(), record.getUnitTargetId()))
                .findFirst().orElse(null);
    }

}
